/* ----------------------------- Noble Steed Engine----------------------------*
Authors : Daniel Randle, Alex Bourne
Date Created : Mar 5 2013

File:
	nsshader.h

Description:
	This file contains the shader class which can initialize/load/bind shaders
	from file or c style string
*---------------------------------------------------------------------------*/

#ifndef NSSHADER_H
#define NSSHADER_H


#include <myGL\glew.h>
#include <global.h>
#include <string>
#include <vector>
#include <nsmath3d.h>

/* ---------------------------------Class NSShader-----------------------------*

Description:
	NSShader will laod a shader in to memory so that its ready to execute on creation.
	When the shader is destroyed it will free the memory, but until then it keeps
	it. Use enable and disable to enable and disable drawing with the shader.

*-------------------------------------------------------------------------------*/

// Forward declarations
#ifdef DEBUG
class Debug;
#endif

class NSDirectionLight;

class NSShader
{
public:
	enum Error { NONE, PROGRAM, FILE, SHADER_TYPE, COMPILE, LINK, VALIDATE };
	enum ShaderType { VERTEX, GEOMETRY, FRAGMENT };
	enum ShaderProgType {BASE, LIGHT, SHADOW, PICKING};

	NSShader(const std::string & _shaderID, const std::string & fragmentFileName, const std::string & vertexFileName, const std::string & geometryFileName = "", const std::string & shaderDirectory = "Shaders");
	~NSShader();

	bool loadFromFile(const std::string & fragmentFileName="", const std::string & vertexFileName="", const std::string & geometryFileName="");

	void setAsTransformShader(const std::vector<std::string> & outVars);

	void setUniform(const char * variableName, const NSMatrix4Df & data) const;
	void setUniform(const char * variableName, const NSMatrix3Df & data) const;
	void setUniform(const char * variableName, const NSVec4Df & data) const;
	void setUniform(const char * variableName, const NSVec3Df & data) const;
	void setUniform(const char * variableName, const NSVec2Df & data) const;
	void setUniform(const char * variableName, float dat) const;
	void setUniform(const char * variableName, int data) const;
	void setUniform(const char * variableName, unsigned int data) const;

	virtual void updateUniforms();

	void enable() const;
	void disable() const;

	void resetErrorState();
	void setShaderID(const std::string & _shaderID);

	Error getErrorState() const;
	GLuint getProgID() const;
	std::string getShaderID() const;
	unsigned int getAttributeIndex(const char * name) const;
	ShaderProgType getProgramType() const;

	unsigned int & objectsUsing();

	#ifdef DEBUG
	void setDebug(Debug *_d);
	#endif

protected:
	std::string _getSource(const std::string & fileName);
	GLuint _loadSource(const std::string & text, ShaderType type = FRAGMENT);
	bool _linkShaders(GLuint fragShaderID, GLuint vertShaderID, GLuint geomShaderID);
	bool _validate();
	void _setupTransformFeedback();

	GLuint progID;
	Error errorState;
	ShaderProgType sProgType;
	std::string shaderID;
	
	std::string fragFileName;
	std::string vertFileName;
	std::string geomFileName;
	std::string shaderDir;

	std::vector<std::string> outs;

	unsigned int objUse;


	#ifdef DEBUG
	Debug *d;
	#endif
};


class NSLightShader : public NSShader
{

public:
	NSLightShader(const std::string & _shaderID, 
		const std::string & fragmentFileName, 
		const std::string & vertexFileName, 
		const std::string & geometryFileName = "", 
		const std::string & shaderDirectory = "Shaders");
	~NSLightShader();

	void updateUniforms();
};

class NSShadowShader : public NSShader
{
public:
	NSShadowShader(const std::string & _shaderID, 
		const std::string & fragmentFileName, 
		const std::string & vertexFileName, 
		const std::string & geometryFileName = "", 
		const std::string & shaderDirectory = "Shaders");

private:
};



class NSPickingShader : public NSShader
{
public:
	NSPickingShader(const std::string & _shaderID, 
		const std::string & fragmentFileName, 
		const std::string & vertexFileName, 
		const std::string & geometryFileName = "", 
		const std::string & shaderDirectory = "Shaders");

private:
};

#endif